#pragma once

#include "ConnectionHandler.hpp"
#include "FdSetting.hpp"
#include <iostream>
#include <functional>
#include <cerrno>
#include <cstring>
#include <netinet/in.h>  // for sockaddr_in
#include <arpa/inet.h>
#include <sys/socket.h>

// for connection manage
class Accepter
{
public:
    Accepter() = default;

    static void acceptConnection(Connection::ptr conn)
    {
        while(true)
        {
            struct sockaddr_in client_addr;
            socklen_t len = sizeof(client_addr);
            int client_fd = ::accept(conn->getFd(), reinterpret_cast<sockaddr*>(&client_addr), &len);

            if(client_fd > 0)
            {
                std::cout << "Accept a connection from " << inet_ntoa(client_addr.sin_addr) << ":" 
                        << ntohs(client_addr.sin_port) << ", fd: " << client_fd << std::endl;
                
                // set non-blocking
                FdSetting::setNonBlock(client_fd);

                // build connection
                // 1) create r/w/e callback function of connection
                auto read_func = std::bind(&ConnectionHandler::recver, std::placeholders::_1);
                auto write_func = std::bind(&ConnectionHandler::sender, std::placeholders::_1);
                auto error_func = std::bind(&ConnectionHandler::excepter, std::placeholders::_1);
                // 2) create connection object
                uint32_t events = EPOLLIN | EPOLLET;  // EPOLLET: edge trigger
                auto client_conn = ConnectionFactory::buildNormalConnection(
                    client_fd, events, conn->getReactor(), read_func, write_func, error_func);
                
                // add connection to reactor for being managed
                conn->getReactor()->addConnection(client_conn);  // add into connection map and kernel epoll
            }
            else
            {
                if(errno == EAGAIN)  // no more connection to accept
                {
                    break;
                }
                else if(errno == EINTR)  // signal interrupt
                {
                    continue;
                }
                else
                {
                    // realy error
                    std::cerr << "Accept a new connection, error: " << strerror(errno) << std::endl;
                }
            }
        }
    }
};